{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 读取和存储\n",
    "\n",
    "到目前为止，我们介绍了如何处理数据以及如何构建、训练和测试深度学习模型。然而在实际中，我们有时需要把训练好的模型部署到很多不同的设备。在这种情况下，我们可以把内存中训练好的模型参数存储在硬盘上供后续读取使用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.0.0\n"
     ]
    }
   ],
   "source": [
    "import tensorflow as tf\n",
    "import numpy as np\n",
    "print(tf.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4.5.1 load and save NDarray\n",
    "\n",
    "我们可以直接使用`save`函数和`load`函数分别存储和读取。下面的例子创建了tensor`x`，并将其存在文件名同为`x`的文件里。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: id=2, shape=(3,), dtype=float32, numpy=array([1., 1., 1.], dtype=float32)>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "x = tf.ones(3)\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后我们将数据从存储的文件读回内存。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1., 1., 1.], dtype=float32)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.save('x.npy', x)\n",
    "x2 = np.load('x.npy')\n",
    "x2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们还可以存储一列`tensor`并读回内存。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(<tf.Tensor: id=6, shape=(3,), dtype=float32, numpy=array([1., 1., 1.], dtype=float32)>,\n",
       " <tf.Tensor: id=7, shape=(4,), dtype=float32, numpy=array([0., 0., 0., 0.], dtype=float32)>)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = tf.zeros(4)\n",
    "np.save('xy.npy',[x,y])\n",
    "x2, y2 = np.load('xy.npy', allow_pickle=True)\n",
    "(x2, y2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们甚至可以存储并读取一个从字符串映射到`tensor`的字典。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array({'x': <tf.Tensor: id=8, shape=(3,), dtype=float32, numpy=array([1., 1., 1.], dtype=float32)>, 'y': <tf.Tensor: id=9, shape=(4,), dtype=float32, numpy=array([0., 0., 0., 0.], dtype=float32)>},\n",
       "      dtype=object)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mydict = {'x': x, 'y': y}\n",
    "np.save('mydict.npy', mydict)\n",
    "mydict2 = np.load('mydict.npy', allow_pickle=True)\n",
    "mydict2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4.5.2 load and save model parameters\n",
    "\n",
    "我们还可以读写模型的参数。\n",
    "为了演示方便，我们先创建一个多层感知机，并将其初始化。。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: id=15, shape=(2, 20), dtype=float32, numpy=\n",
       "array([[ 1.4838032 , -0.638382  , -0.7836789 ,  1.8679693 , -0.73148364,\n",
       "        -0.12649764, -0.2709544 , -0.33071974,  0.08754155, -0.11141171,\n",
       "         0.18274567, -0.64928424, -0.6519136 ,  0.07320689, -0.5973234 ,\n",
       "         1.9181312 ,  0.47066143, -0.10463867, -0.48717928,  0.3107364 ],\n",
       "       [ 0.37838233,  0.11170077, -1.3378098 ,  0.3618399 ,  0.27140674,\n",
       "         0.9901546 ,  1.4799279 ,  1.2373866 , -0.62953895, -1.5107338 ,\n",
       "        -1.6658096 , -0.08139827,  0.5444429 ,  0.94359463, -0.00676966,\n",
       "        -1.5311289 , -0.30671307,  0.38309866, -0.2765001 , -0.61528987]],\n",
       "      dtype=float32)>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X = tf.random.normal((2,20))\n",
    "X"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: id=71, shape=(2, 10), dtype=float32, numpy=\n",
       "array([[-0.30077258,  0.4493576 ,  0.00761353, -0.14657806, -0.11702831,\n",
       "        -0.20244044,  0.15949515, -0.025849  , -0.36856648,  0.23903428],\n",
       "       [-0.09660852,  0.0096112 ,  0.3435048 , -0.066409  , -0.24335058,\n",
       "        -0.01852736,  0.77680373, -0.04183513, -0.232623  , -0.5856861 ]],\n",
       "      dtype=float32)>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class MLP(tf.keras.Model):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.flatten = tf.keras.layers.Flatten()    # Flatten层将除第一维（batch_size）以外的维度展平\n",
    "        self.dense1 = tf.keras.layers.Dense(units=256, activation=tf.nn.relu)\n",
    "        self.dense2 = tf.keras.layers.Dense(units=10)\n",
    "\n",
    "    def call(self, inputs):         \n",
    "        x = self.flatten(inputs)   \n",
    "        x = self.dense1(x)    \n",
    "        output = self.dense2(x)     \n",
    "        return output\n",
    "\n",
    "net = MLP()\n",
    "Y = net(X)\n",
    "Y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面把该模型的参数存成文件，文件名为`4.5saved_model.h5`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.save_weights(\"4.5saved_model.h5\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，我们再实例化一次定义好的多层感知机。与随机初始化模型参数不同，我们在这里直接读取保存在文件里的参数。\n",
    "\n",
    "因为这两个实例都有同样的模型参数，那么对同一个输入X的计算结果将会是一样的。我们来验证一下。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: id=146, shape=(2, 10), dtype=bool, numpy=\n",
       "array([[ True,  True,  True,  True,  True,  True,  True,  True,  True,\n",
       "         True],\n",
       "       [ True,  True,  True,  True,  True,  True,  True,  True,  True,\n",
       "         True]])>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "net2 = MLP()\n",
    "net2(X)\n",
    "net2.load_weights(\"4.5saved_model.h5\")\n",
    "Y2 = net2(X)\n",
    "Y2 == Y"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "tf2.0",
   "language": "python",
   "name": "tf2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
